/**
 * @file Rs485_transfer.c
 * @author Chen Jiale (530673764@qq.com)
 * @brief RS485传输层
 * @version V1.0.0
 * @date 2023-12-09
 * 
 * @copyright Moorgen Intelligent Technologies Copyright (c) 2023
 * 
 */
#include "Rs485_transfer.h"	
#include "Rs485_bsp.h"
#include <includes.h>
//接收数据结构体
typedef struct
{
	uint8_t ptrBufWrite;			//接收数据下标
	uint8_t Buf[RS485_R_DATA_LEN];	//接收数据
	uint8_t Length;					//接收字节长度
	uint8_t TimeCnt;				//接收间隔计时
	bool TimeEnble;					//接收定义使能
	bool RxLock;					//接收锁定
} STRUCT_RS485_RECEIVE;
volatile STRUCT_RS485_RECEIVE	Rs485_RxData;//接收数据结构体
volatile uint8_t Recv_data_fifo[RS485_RBUF_FIFO_SIZE];//缓存数组
volatile uint8_t Recv_data_frame_len = 0;    //接收一帧数据长度
volatile int32_t Recv_data_fifo_len = 0; //当前有数据长度do_bus_recv_buf_count

//发送数据结构体
typedef struct
{
	bool Stat;						//发送状态
	bool Txing_Sate;				//硬件发送状态
	uint8_t Length;					//单包发送长度
	uint8_t Buf[RS485_T_DATA_LEN];	//单包发送缓存
	bool Busy;						//总线状态
	bool BackTimerEnble;			//退避定时器使能
	uint8_t BackTimeCnt;			//退避计时
	uint16_t WaitTime;				//退避时间
	uint16_t WaitCnt;				//退避等待次数
	bool AckEnble;					//应答使能
	uint16_t ReTxEnable;			//重发触发标记
	uint16_t ReTxCount;				//重发计数器
	uint16_t ReTxNum;				//重发总数
	uint16_t ReTxTimeCnt;			//重发等待时间
	uint16_t ReTxTime;				//重发定时
} STRUCT_RS485_SEND;
volatile STRUCT_RS485_SEND		Rs485_TxData;//发送数据结构体
volatile uint8_t Send_data_fifo[RS485_SBUF_FIFO_SIZE];//缓存数组
volatile uint8_t Send_data_frame_len = 0;	//接收一帧数据长度
volatile uint32_t Send_data_fifo_len = 0; 	//当前有数据长度

/**
 * @brief 根据波特率计算退避基时间（ms）
 * 
 * @param aBaud 波特率
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 * @return uint8_t 
 */
static uint8_t Rs485_GetBaseWaitTime(enumBaudType aBaud)
{
	switch(aBaud)
	{
	case BAUD_38400:
		return 30;
	case BAUD_19200:
		return 40;
	case BAUD_9600:
		return 135;
	default:
		return 40;
	}
}
/**
 * @brief 获取随机退避时间
 * 
 * @param Sys_Addr 设备地址
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 * @return uint8_t 
 */
static uint8_t Rs485_GetRandTime(uint8_t Sys_Addr)
{
	//退避时间等于：随机数＋设备地址＋一个基时间
	return ((uint8_t)SysTick->VAL % 20) + Sys_Addr % 40 + Rs485_GetBaseWaitTime(USART_BAUDRATE);
}

/**
 * @brief 使能发送时钟
 * 
 * @param tm 退避时间
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 */
static void Rs485_SendTimeEnable(uint16_t tm)
{
	Rs485_TxData.BackTimeCnt 	= tm;
	Rs485_TxData.BackTimerEnble 	= true;
}

/**
 * @brief 停止发送时钟
 * 
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 */
static void Rs485_SendTimeDisable(void)
{
	Rs485_TxData.BackTimerEnble = false;
}

/**
 * @brief 总线是否繁忙标记
 * 
 * @param Is_Busy 是否繁忙
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 */
void Rs485_Busy_Set(bool Is_Busy)
{
	Rs485_TxData.Busy = Is_Busy;
//	printf("Rs485_TxData.Busy Is_Busy\n");
}

/**
 * @brief 设置发送状态标记
 * 
 * @param Is_Txing 底层硬件是否正在发送
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 */
void Rs485_SendData_Set(bool Is_Txing)
{
	Rs485_TxData.Txing_Sate = Is_Txing;
}

/**
 * @brief 停止发送进程
 * 
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 */
static void Rs485_SendProcessStop(void)
{
	Rs485_SendTimeDisable();
	Rs485_TxData.ReTxEnable = false;
	Rs485_TxData.AckEnble = false;
	Rs485_TxData.Stat = false;
}

/**
 * @brief 发送准备，检测退避
 * 
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 */
static void Rs485_SendTimerReady(void)
{
	Rs485_TxData.ReTxTimeCnt = 0;
	Rs485_TxData.WaitCnt 	= RS485_WAIT_CNT;//退避等待次数
	Rs485_TxData.Busy 	= false;   //总线状态

	if(Rs485_TxData.ReTxNum == 0xF)//一直重发
	{
		//重发时间间隔计算(10ms/per)
		if(Rs485_TxData.ReTxCount <= 1475)
		{
			Rs485_TxData.ReTxTime = RS485_RESEND_WAIT_TIME +	Rs485_TxData.ReTxCount * 4;
			Rs485_TxData.ReTxCount++;
		}
		else
		{
			Rs485_TxData.ReTxTime = 60000;//10分钟
		}
	}
	else//指定次数重发
	{
		if(Rs485_TxData.ReTxCount < Rs485_TxData.ReTxNum)
		{
			Rs485_TxData.ReTxTime = RS485_RESEND_WAIT_TIME +	Rs485_TxData.ReTxCount * 4;
			Rs485_TxData.ReTxCount++;
		}
		else
		{
			Rs485_SendProcessStop();
		}
	}

	if(Rs485_TxData.Buf[0]  == 0x03)
		Rs485_TxData.WaitTime = Rs485_GetBaseWaitTime(USART_BAUDRATE);//应答包时间固定
	else
		Rs485_TxData.WaitTime = Rs485_GetRandTime(0xFF) ;//退避时间
	//使能发送时钟(us)
	Rs485_SendTimeEnable(Rs485_TxData.WaitTime);

}

/**
 * @brief 发送数据推入缓存
 * 
 * @param pbuf 发送数据指针
 * @param len 发送数据长度
 * @param wait_ack 是否等待应答
 * @param Resend_Time 重发次数（0x7F会一直重发，可用于注册）
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 * @return int32_t 剩余缓存数据长度，-1 表示缓存已满
 */
int32_t Rs485_Send_Data_Push(uint8_t *pbuf, uint8_t len, bool wait_ack,uint8_t Resend_Time)
{
	int32_t i = Send_data_fifo_len;
	uint8_t *ptr = (uint8_t *)Send_data_fifo;

	if (i + 2 + len >= RS485_SBUF_FIFO_SIZE)//超出缓存丢弃
		return -1;
	ptr[i + 0] = len;					//数据长度
	ptr[i + 1] = wait_ack<<7 | Resend_Time;	//Bit7:是否需要应答|Bit0-4发送次数
	memcpy(i + ptr + 2, pbuf, len);		//接收数据
	i += 2 + len;						//数据长度增加
	Send_data_fifo_len = i;				//数据长度

	return i;
}
/**
 * @brief 发送数据插队，将数据插入到缓存的开头
 * 
 * @param pbuf 发送数据指针
 * @param len 发送数据长度
 * @param wait_ack 是否等待应答
 * @param Resend_Time 重发次数（0x7F会一直重发，可用于注册）
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 * @return int32_t 剩余缓存数据长度，-1 表示缓存已满
 */
int32_t Rs485_Send_Data_Push_Front(uint8_t *pbuf, uint8_t len, bool wait_ack, uint8_t Resend_Time)
{
    int32_t i = Send_data_fifo_len;
	uint8_t *ptr = (uint8_t *)Send_data_fifo;

    if (i + 2 + len >= RS485_SBUF_FIFO_SIZE)	// 超出缓存丢弃
        return -1;
    memmove(ptr + 2 + len, ptr, i); 			// 将原有数据向后移动
    ptr[0] = len;                               // 数据长度
    ptr[1] = (wait_ack << 7) | (Resend_Time & 0x1F); // Bit7:是否需要应答|Bit0-4发送次数
    memcpy(ptr + 2, pbuf, len);                 // 插入新数据
	i += 2 + len;								//数据长度增加
    Send_data_fifo_len = i;						// 更新数据长度

	return i;
}
/**
 * @brief 发送数据从缓存取出
 * 
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 * @return int32_t 剩余缓存数据长度
 */

int32_t Rs485_Send_Data_Pop(void)
{
	int32_t i = Send_data_fifo_len;
	uint8_t *ptr = (uint8_t *)Send_data_fifo;
	uint8_t len = ptr[0];

	if (2 + len > i)
		i = 0;
	else
	{
		memcpy(ptr, ptr + 2 + len, RS485_SBUF_FIFO_SIZE - (2 + len));
		i -= 2 + len;
	}
	Send_data_fifo_len = i;

	return i;
}

/**
 * @brief 485发送进程（轮询调用）
 * 
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 * @return true 有数据需要发送
 * @return false 无数据需要发送
 */
bool Rs485_SendProcess(void)
{
	uint8_t len,iii;
	uint8_t *buf = (uint8_t *)Send_data_fifo;	//数组
	uint8_t i = Send_data_fifo_len;				//长度
	
	if(Rs485_TxData.Stat)return false;//发送状态
	len = buf[0];							//数据长度
	if(i == 0)//缓存无数据
		return false;
	if(len >= RS485_T_DATA_LEN)
	{
		Rs485_TxData.Stat = false;
		Rs485_Send_Data_Pop();
		return false;
	}
	Rs485_TxData.Stat		= true;
	Rs485_TxData.Length 		= len;		//长度
	memcpy((uint8_t*)Rs485_TxData.Buf,&buf[2],Rs485_TxData.Length);	//数据

	Rs485_TxData.AckEnble	= buf[1]>>7;//应答使能
	Rs485_TxData.ReTxNum		= buf[1]&0xF;//重发总数
	Rs485_TxData.ReTxCount	= 0;		//重发计数器
	Rs485_SendTimerReady();				//进入发送准备
	Rs485_Send_Data_Pop();				//删除发送数据
	return true;
}

/**
 * @brief 发送数据定时进程
 * 
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 */
extern char sync_lianpai_light ;
void Rs485_SendProcess_Timer(void)
{
	volatile uint8_t iii,SendData_Temp[RS485_T_DATA_LEN];
	if(!Rs485_TxData.BackTimerEnble)return;// 退避定时器使能
	if(Rs485_TxData.BackTimeCnt)//退避中
	{
		Rs485_TxData.BackTimeCnt--;
		return;
	}
	Rs485_TxData.BackTimeCnt = Rs485_TxData.WaitTime;
	if(Rs485_TxData.Busy == true)
	{
		Rs485_TxData.Busy = false;
		if(Rs485_TxData.WaitCnt)//总线忙等待
			Rs485_TxData.WaitCnt--;
		else
		{
			Rs485_TxData.WaitCnt = 0;
			Rs485_TxData.ReTxTimeCnt = 0;
			if(Rs485_TxData.AckEnble)
			{
				//关闭发送时钟(us)
				Rs485_TxData.ReTxEnable = true;
				Rs485_SendTimeDisable();
			}
			else
			{
				Rs485_SendProcessStop(); //结束
			}
		}
	}
	else
	{	
		memcpy(SendData_Temp,(uint8_t*)Rs485_TxData.Buf,Rs485_TxData.Length);
		if(Rs485_TxData.Buf[0] == 0x04 || Rs485_TxData.Buf[0] == 0x27)
		{
			for (iii = 0; iii < Rs485_TxData.Length; iii++)
			{
				printf("%02X ", Rs485_TxData.Buf[iii]);
			}
			printf(", Data Len:%d  Rs485_TxData\n", Rs485_TxData.Length);	
		}			
		Rs485_Send(SendData_Temp,Rs485_TxData.Length);
		Rs485_TxData.WaitCnt = 0;
		Rs485_TxData.ReTxTimeCnt = 0;
		if(sync_lianpai_light == 1 && Rs485_TxData.Buf[0] == 0x50)
		{
				sync_lianpai_light = 0;
				if(Light.flag!=0)Light.flag=0;//clf 
				TIM_Mode_Config(LIGHT_PWM_MAX);					
		}	
		if(Rs485_TxData.AckEnble)
			Rs485_SendTimeDisable();
		else
			Rs485_SendProcessStop();//结束发送进程
	}
}

/**
 * @brief 重发数据定时进程(10ms调用)
 * 
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 */
void Rs485_ReSendProcess_Timer(void)
{
	if(!Rs485_TxData.Stat)return;
	if(!Rs485_TxData.AckEnble)return;//应答使能
	if(Rs485_TxData.BackTimerEnble)return;

	Rs485_TxData.ReTxTimeCnt++;
	if(Rs485_TxData.ReTxTimeCnt < Rs485_TxData.ReTxTime)return;
	Rs485_TxData.ReTxTimeCnt = 0;
	Rs485_TxData.ReTxEnable = true;
}

/**
 * @brief 重发进程(轮询调用)
 * 
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 */
void Rs485_ReSendProcess(void)
{
	if(!Rs485_TxData.ReTxEnable)return;
	Rs485_SendTimerReady();//发数
	Rs485_TxData.ReTxEnable = false;
}
//===================================< 接收数据 >===================================//
/**
 * @brief 清理接收结构体
 * 
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 */
void Rs485_Rx_Clear(void)
{
	memset((uint8_t*)&Rs485_RxData,0,sizeof(Rs485_RxData));
}

/**
 * @brief 接收数据放入缓存
 * 
 * @param pbuf 接收的数据指针
 * @param len 数据长度
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 * @return int32_t 
 */
static int32_t Rs485_Recv_Data_Push(uint8_t *pbuf, uint8_t len)
{
	int32_t i = Recv_data_fifo_len;
	uint8_t *ptr = (uint8_t *)Recv_data_fifo;

	if (i + 2 + len >= RS485_RBUF_FIFO_SIZE)//超出缓存，丢弃
		return -1;
	ptr[i + 0] = len;					//数据长度
	ptr[i + 1] = 0;						//标记
	memcpy(i + ptr + 2, pbuf, len);		//接收数据
	i += 2 + len;						//数据长度增加
	Recv_data_fifo_len = i;				//数据长度
	return i;
}

/**
 * @brief 接收数据从缓存取出
 * 
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 * @return int32_t 
 */
static int32_t Rs485_Recv_Data_Pop(void)
{
	int32_t i = Recv_data_fifo_len;
	uint8_t *ptr = (uint8_t *)Recv_data_fifo;
	uint8_t len = ptr[0];

	if (2 + len > i)
		i = 0;
	else
	{
		memcpy(ptr, ptr + 2 + len, RS485_RBUF_FIFO_SIZE - (2 + len));
		i -= 2 + len;
	}
	Recv_data_fifo_len = i;
	return i;
}
/*验证将要放入缓存的数据*/
/**
 * @brief 检查收到的485数据，有效数据推入缓存
 * 
 * @param pbuf 接收的数据指针
 * @param len 数据长度
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 * @return int32_t 
 */
uint8_t  Crc8CheckSum(uint8_t *ptr,uint16_t length);
static int32_t Rs485_Check_Recv(uint8_t *pbuf, uint32_t len)
{
	uint8_t cmd   	 = pbuf[0];
	uint8_t target  = pbuf[2];
	uint8_t frame_id = 0; 
	uint8_t crc = 0;
	if (len < 7) //检验长度
	{
		printf("on rs485_transref len is err\n");			  		
		return -1;
	}
	if (cmd == 0x00 || cmd == 0x50)
	{
		return Rs485_Recv_Data_Push(pbuf, len);
	}		
	if(cmd> 0xFa)
	{		
		printf("on rs485_transref cmd is err\n");			  		
		return -2;	
	}

	if(target != Dev.addr[0])	//设备地址
	{
        if (cmd == 0x08 && target == 0xFF && pbuf[5] == 0x88 && pbuf[7] ==0xAA &&\
            pbuf[8] ==0xBB && pbuf[9] ==0xBB)
        {   }
        else
        {
            printf("not myself adress \n");			  		
            return -4;
        }
	}	
	crc = Crc8CheckSum(pbuf, len - 1);
	if (crc != pbuf[len - 1]) //检验CRC
	{
		printf("on rs485_transref crc is err\n");			  		
		return -3;
	}
	if (cmd == 0x03)
	{
		Rs485_SendProcessStop();
		return 0;
	}
	frame_id = pbuf[3];
	
	Rs485_Recv_Data_Push(pbuf, len);
	IT1_Load_RS485_Ack(frame_id,OPT_OK);
	return 0;
}
/**
 * @brief 接收分包定时(1ms)
 * 
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 */
void Rs485_RxOverTimIsr(void)
{
	if(Rs485_RxData.TimeEnble == false)return ;
	Rs485_RxData.TimeCnt++;
	if(Rs485_RxData.TimeCnt < RS485_RXEND_TIME)return;//当RS485_RXEND_TIME时间内没有收到字节，接收结束
	Rs485_RxData.Length = Rs485_RxData.ptrBufWrite;//保存结束帧长度，防止中断改变
	Rs485_Check_Recv((uint8_t*)Rs485_RxData.Buf, Rs485_RxData.Length);
	//接收完成,清理接收结构体
	Rs485_Rx_Clear();
}

//串口接收中断(接收被硬件上永远使能,会收到自己发的数)
/**
 * @brief 串口接收字节(中断调用)
 * 
 * @param Recv_Byte 接收到的字节
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 */
void Rs485_Receive_Byte(uint8_t Recv_Byte)
{
	Rs485_TxData.Busy = true;
	if(Rs485_TxData.Txing_Sate)		
	{
		return;
	}
	//使能接收完成计数器
	Rs485_RxData.TimeCnt   = 0;
	Rs485_RxData.TimeEnble = true;	
	//保存字节
	Rs485_RxData.Buf[Rs485_RxData.ptrBufWrite] = Recv_Byte;
	Rs485_RxData.ptrBufWrite++;
	if(Rs485_RxData.ptrBufWrite >= RS485_R_DATA_LEN)
	{
		Rs485_RxData.RxLock = true;
		Rs485_RxData.ptrBufWrite = RS485_R_DATA_LEN;
	}
}

/**
 * @brief Rs485接收进程(轮询调用)
 * 
 * @param pbuf 接收数据存放指针
 * @param len 接收数据长度存放变量指针
 * @author Chen Jiale (530673764@qq.com)
 * @date 2023-12-09
 * @return true 存在接收缓存数据
 * @return false 无数据
 */
bool Rs485_RecvProcess(uint8_t *pbuf,uint8_t *len)
{
	uint8_t i = Recv_data_fifo_len;
	uint8_t *fifo_buf = (uint8_t *)Recv_data_fifo;
	*len = fifo_buf[0];//单包数据长度
	if(i == 0)//缓存无数据
		return false;
	//存在数据
	memcpy(pbuf,fifo_buf + 2,*len);
	Rs485_Recv_Data_Pop();
	return true;
}

